package com.heo.server;

import static io.netty.handler.codec.http.HttpHeaderNames.CONNECTION;
import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH;
import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE;
import static io.netty.handler.codec.http.HttpResponseStatus.CONTINUE;
import static io.netty.handler.codec.http.HttpResponseStatus.OK;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
import java.net.InetSocketAddress;
import java.util.HashMap;
import org.apache.log4j.Logger;
import org.springframework.util.StringUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.heo.pojo.ControlInfoEntity;
import com.heo.pojo.DeviceInfoEntity;
import com.heo.pojo.DeviceModelEntity;
import com.heo.pojo.MessageEntity;
import com.heo.pojo.OrgInfoEntity;
import com.heo.protocol.packet.DeviceCommand;
import com.heo.protocol.packet.RebootDeviceReq;
import com.heo.protocol.packet.StartDeviceReq;
import com.heo.pub.ApplicationContextUtil;
import com.heo.pub.DictEnum;
import com.heo.pub.ServerConfig;
import com.heo.queue.TaskQueue;
import com.heo.queue.task.SendTask;
import com.heo.service.DeviceInfoService;
import com.heo.service.DeviceModelService;
import com.heo.service.OrgInfoService;
import com.heo.thread.SendMessageTransparentThread;
import com.heo.util.ByteHelper;
import com.heo.util.NettyChannelMapUtils;
import com.heo.util.RemoteUpgrade;
import com.heo.util.SequenceHelper;
import com.heo.util.Utils;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaderValues;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpUtil;
import io.netty.util.AsciiString;
import io.netty.util.CharsetUtil;

public class ControllerServerHandler extends SimpleChannelInboundHandler<FullHttpRequest> {
	private static Logger log = Logger.getLogger(ConsoleServerHandler.class);
	private static final AsciiString KEEP_ALIVE = new AsciiString("keep-alive");

	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		// 判断是否是符合要求的ip地址连接上来。如果是，就建立连接，否则就断开
		String remoteIpAddress = ((InetSocketAddress) ctx.channel().remoteAddress()).getAddress().getHostAddress();
		if (ServerConfig.CONTROL_IP != null && ServerConfig.CONTROL_IP.length > 0) {
			for (int i = 0; i < ServerConfig.CONTROL_IP.length; i++) {
				if (remoteIpAddress.equals(ServerConfig.CONTROL_IP[i].trim())) {
					return;
				}
			}
			ctx.channel().close().addListener(new ChannelFutureListener() {
				// 通道关闭后，触发本事件
				@Override
				public void operationComplete(ChannelFuture future) throws Exception {
					log.error("[server][controller][" + remoteIpAddress + "]访问控制台的IP不在许可的IP范围，链接已断开。");
				}
			});
		}
	}

	@Override
	public void channelRead0(ChannelHandlerContext ctx, FullHttpRequest req) throws Exception {
		String channelId = ctx.channel().id().asShortText();// 连接ID
		String remoteIpAddress = ((InetSocketAddress) ctx.channel().remoteAddress()).getAddress().getHostAddress();
		String logHeader = "[server][controller][" + remoteIpAddress + "]channelId:" + channelId;
		String content = "";
		try {
			if (HttpUtil.is100ContinueExpected(req)) {
				ctx.write(new DefaultFullHttpResponse(HTTP_1_1, CONTINUE));
			}
			if (req.uri().equalsIgnoreCase("/control.do")) {
				if (!"POST".equals(req.method().name())) {
					MessageEntity MessageEntity = new MessageEntity(DictEnum.MessageEntity.REQUEST_TYPE_ERROR,
							"请求类型错误，请使用POST方式提交。");
					content = JSON.toJSONString(MessageEntity);
					log.debug(logHeader + "请求类型错误，请使用POST方式提交。");
				} else if (StringUtils.isEmpty(req.content().toString(CharsetUtil.UTF_8))) {
					MessageEntity MessageEntity = new MessageEntity(DictEnum.MessageEntity.REQUEST_PARA_ERROR,
							"请求参数错误");
					content = JSON.toJSONString(MessageEntity);
					log.debug(logHeader + "请求参数错误。");
				} else {
					// 开始业务处理
					content = req.content().toString(CharsetUtil.UTF_8);
					log.debug(logHeader + "收到控制指令：" + content);
					JSONObject json = JSON.parseObject(content);
					String deviceIMEI = json.getString("deviceIMEI");
					String appId = json.getString("appId");
					// 从redis里取到该设备的信息
					ControlInfoEntity controlInfo = NettyChannelMapUtils.getDeviceControlInfoByIEMI(appId, deviceIMEI);
					if (controlInfo == null) {
						MessageEntity MessageEntity = new MessageEntity(DictEnum.MessageEntity.DEVICE_IS_NOT_ONLINE,
								"设备不在线");
						content = JSON.toJSONString(MessageEntity);
						log.debug(logHeader + "操作失败，设备不在线。");
						NettyChannelMapUtils.remove(appId, deviceIMEI);
					} else {
						// 开始执行来自对设备的控制指令
						int commandId = json.getIntValue("commandId");

						// 查这个设备的具体信息
						DeviceInfoService service = (DeviceInfoService) ApplicationContextUtil
								.getBeanById("deviceInfoService");
						HashMap<String, Object> paraMap = new HashMap<String, Object>();
						paraMap.put("deviceImei", deviceIMEI);

						DeviceInfoEntity deviceInfoEntity = service.getByKey(deviceIMEI);

						if (deviceInfoEntity == null) {
							MessageEntity MessageEntity = new MessageEntity(DictEnum.MessageEntity.DEVICE_IS_NOT_EXIST,
									"设备不存在");
							content = JSON.toJSONString(MessageEntity);
							log.debug(logHeader + "操作失败，设备不存在。");
						} else {
							switch (commandId) {
							// 启动设备
							case DeviceCommand.START_DEVICE_REQ:
								log.debug(logHeader + "收到启动设备指令。");
								content = startDevice(logHeader, json, appId, deviceIMEI, deviceInfoEntity);
								break;
							// 固件升级通知
							case DeviceCommand.FIRMWARE_NOTICE_REQ:
								log.debug(logHeader + "收到固件升级通知指令。");
								content = updateFirewareNotice(logHeader, json, appId, deviceIMEI, deviceInfoEntity);
								break;
							// 重新启动设备
							case DeviceCommand.REBOOT_DEVICE_REQ:
								log.debug(logHeader + "收到重新启动设备指令。");
								content = rebootDevice(appId, logHeader, json, deviceIMEI, deviceInfoEntity);
								break;
							default:
								MessageEntity MessageEntity = new MessageEntity(
										DictEnum.MessageEntity.UNKNOW_COMMAND_ID, "未知的指令类型");
								content = JSON.toJSONString(MessageEntity);
								log.debug(logHeader + "未知的指令类型。");
								break;
							}
						}
					}
				}
			} else {
				// 发送404错误应答头
				content = "404";
			}

			boolean keepAlive = HttpUtil.isKeepAlive(req);
			// boolean keepAlive = false;

			// 向浏览器写入数据流
			FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK,
					Unpooled.wrappedBuffer(content.getBytes("UTF-8")));

			if ("404".equals(content)) {
				response.setStatus(HttpResponseStatus.NOT_FOUND);
			} else {
				response.setStatus(HttpResponseStatus.OK);
			}

			response.headers().set(CONTENT_TYPE, "text/html; charset=utf-8");
			response.headers().setInt(CONTENT_LENGTH, response.content().readableBytes());

			if (!keepAlive) {
				ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
			} else {
				response.headers().set(CONNECTION, HttpHeaderValues.KEEP_ALIVE);
				response.headers().set(KEEP_ALIVE, "timeout=300,max=100");
				ctx.writeAndFlush(response);
			}
			ctx.close();
		} catch (Exception e) {
			log.error(e.getMessage(), e);
			content = e.getMessage();
			// throw e;
		} finally {
			// 对于不再使用的ByteBuf引用计数类型，需要手动释放。
			// http://netty.io/wiki/reference-counted-objects.html
			// ReferenceCountUtil.release(req);
		}
	}

	private String startDevice(String logHeader, JSONObject json, String appId, String deviceIMEI,
			DeviceInfoEntity deviceInfoEntity) throws Exception {
		String content;
		int channelIndex = json.getIntValue("deviceChannelIndex");// 通道编号
		String startPara = StringUtils.isEmpty(json.getString("startPara")) ? null : json.getString("startPara").trim();
		String callbackPara = StringUtils.isEmpty(json.getString("callbackPara")) ? null
				: json.getString("callbackPara").trim();
		int workingDuration = json.getIntValue("callbackExpireTime") <= 0 ? Integer.MAX_VALUE
				: json.getIntValue("callbackExpireTime");// 设备预计的工作时间
		if (!Utils.checkNull(startPara)) {
			MessageEntity MessageEntity = new MessageEntity(DictEnum.MessageEntity.COMMAND_ERROR,
					"指令格式错误，startPara不能为NULL。");
			content = JSON.toJSONString(MessageEntity);
			log.debug(logHeader + "指令格式错误，startPara不能为NULL。");
		} else {
			OrgInfoService orgInfoService = (OrgInfoService) ApplicationContextUtil.getBeanById("orgInfoService");
			OrgInfoEntity orgInfoEntity = orgInfoService.getByKey(deviceIMEI);
			String callUrl = orgInfoEntity.getCallUrl();
			int expectedWorkingTime = json.getIntValue("w");
			JSONObject jsonObject = null;
			try {
				jsonObject = JSON.parseObject(json.getString("startPara"));
				String h = jsonObject.getString("h");
				int o = jsonObject.getIntValue("o");
				if(o == 4) {
					if (!Utils.checkNull(h)) {
						jsonObject.put("j", Utils.getBreakTime(expectedWorkingTime));
						jsonObject.put("w", Utils.getWorkTime(expectedWorkingTime));
						//jsonObject.put("t", 35);
						//by:xuyongwen
						jsonObject.put("t", deviceInfoEntity.getHeartInterval());
					} else {
						jsonObject.remove("w");
						jsonObject.remove("j");
					}
				}
				// 艾亚欣充电桩模式 币控
				if (deviceInfoEntity.getDeviceModelId() == DictEnum.ModelType.AIYAXING_CHARGING_PILE) {
					StringBuilder sBuilder = new StringBuilder("EE0D02000000000000");
					int min = Utils.getInt(h);
					sBuilder.append(ByteHelper.intTo16Length2(channelIndex + 1));
					sBuilder.append("0000");
					sBuilder.append(ByteHelper.intTo16Length4(min));
					byte[] bytes = new byte[] { 0 };
					String xor = sBuilder.toString().substring(2);
					bytes = ByteHelper.hexStringToBytes(xor);
					byte xorByte = ByteHelper.getXor(bytes);
//					sBuilder.append(ByteHelper.intTo16Length2(xorByte));
					sBuilder.append(ByteHelper.byteToHexStringNoSpace(new byte[] {xorByte}));
					h = sBuilder.toString();
					jsonObject.put("h", h);
				}
				jsonObject.put("m", deviceInfoEntity.getDeviceMac());
				
//				SendMessageTransparentThread sendMessageTransparentThread = new SendMessageTransparentThread(appId,
//						deviceIMEI, jsonObject.toJSONString(), 0);
//				NettyChannelMapUtils.sendTaskThreadPool.execute(sendMessageTransparentThread);
//				if (Utils.checkNull(callbackPara)) {
//					NettyChannelMapUtils.setCallBackPara(appId, deviceIMEI, channelIndex, callbackPara,
//							workingDuration);
//					NettyChannelMapUtils.callBackSendStatus(appId, deviceIMEI, channelIndex,
//							DictEnum.CallBackType.START_RESULT, "03", callUrl, null, null);
//				}
//				MessageEntity messageEntity = new MessageEntity(DictEnum.MessageEntity.SUCCESS, "指令下发成功");
//				content = JSON.toJSONString(messageEntity);
				
				
//				if(ServerConfig.RESEND_STATUS == 1) {//重发开启状态  0开启重发  1 不开启重发
//					SendMessageTransparentThread sendMessageTransparentThread = new SendMessageTransparentThread(appId,
//							deviceIMEI, jsonObject.toJSONString(), 0);
//					NettyChannelMapUtils.sendTaskThreadPool.execute(sendMessageTransparentThread);
//					if (Utils.checkNull(callbackPara)) {
//						NettyChannelMapUtils.setCallBackPara(appId, deviceIMEI, channelIndex, callbackPara,
//								workingDuration);
//						NettyChannelMapUtils.callBackSendStatus(appId, deviceIMEI, channelIndex,
//								DictEnum.CallBackType.START_RESULT, "03", callUrl, null, null);
//					}
//					MessageEntity messageEntity = new MessageEntity(DictEnum.MessageEntity.SUCCESS, "指令下发成功");
//					content = JSON.toJSONString(messageEntity);
//				}else {
//					
//				}
				
				SendTask st;
			    boolean result;
				byte[] startParaArray = new byte[] { 0 };
				String hardVer = deviceInfoEntity.getCurrentHardVer();
				hardVer = hardVer.replaceAll("[a-zA-Z]","" );
				hardVer = hardVer.replace(".", "");
				// 小章投币脉冲 币控 而且 软件版本低于1.3.0的设备走旧流程，没有启动指令重发功能
				if (deviceInfoEntity.getDeviceModelId() == DictEnum.ModelType.XIAOZHANG_COIN_PILE && Integer.parseInt(hardVer) < 130) {
					SendMessageTransparentThread sendMessageTransparentThread = new SendMessageTransparentThread(appId,
							deviceIMEI, jsonObject.toJSONString(), 0);
					NettyChannelMapUtils.sendTaskThreadPool.execute(sendMessageTransparentThread);
					if (Utils.checkNull(callbackPara)) {
						NettyChannelMapUtils.setCallBackPara(appId, deviceIMEI, channelIndex, callbackPara,
								workingDuration);
						NettyChannelMapUtils.callBackSendStatus(appId, deviceIMEI, channelIndex,
								DictEnum.CallBackType.START_RESULT, "03", callUrl, null, null);
					}
					MessageEntity messageEntity = new MessageEntity(DictEnum.MessageEntity.SUCCESS, "指令下发成功");
					content = JSON.toJSONString(messageEntity);
				}else {
					log.debug(logHeader + "设备指令下发，<"+ deviceIMEI +">进入重发任务队列！");
					//新增启动指令重发机制 by：xuyongwen 20190216
					StartDeviceReq sdr = new StartDeviceReq(channelIndex, startParaArray, SequenceHelper.getId(deviceIMEI), jsonObject.toJSONString());
					st = new SendTask(appId, sdr, deviceInfoEntity, channelIndex, jsonObject.toJSONString());// 一定要new，否则和上面的是一个对象！在发包时会有问题。
					result = TaskQueue.offerSendTask(st, true);// 进队列，由专门的线程来处理下发
					if (result){
						if (Utils.checkNull(callbackPara)) {
							NettyChannelMapUtils.setCallBackPara(appId, deviceIMEI, channelIndex, callbackPara,
									workingDuration);
							NettyChannelMapUtils.callBackSendStatus(appId, deviceIMEI, channelIndex,
									DictEnum.CallBackType.START_RESULT, "03", callUrl, null, null);
						}
						MessageEntity messageEntity = new MessageEntity(DictEnum.MessageEntity.SUCCESS, "指令下发成功");
						content = JSON.toJSONString(messageEntity);
					}else
				    {
				        MessageEntity MessageEntity = new MessageEntity(DictEnum.MessageEntity.QUEUE_IS_FULL, "指令下发失败，队列已满，请重试。");
				        content = JSON.toJSONString(MessageEntity);
				        log.debug(logHeader + "指令下发失败，队列已满，请重试。");
				    }
				}
				
			} catch (Exception e) {
				throw new Exception("指令json格式错误");
			}
		}
		return content;
	}

	private String updateFirewareNotice(String logHeader, JSONObject json, String appId, String deviceIMEI,
			DeviceInfoEntity deviceInfoEntity) throws Exception {
		// 查询出设备的升级固件信息
		DeviceModelService service = (DeviceModelService) ApplicationContextUtil.getBeanById("deviceModelService");
		HashMap<String, Object> paraMap = new HashMap<String, Object>();
		paraMap.put("deviceImei", deviceIMEI);

		DeviceModelEntity deviceModel = service.getByMap(paraMap, "getByDeviceIMEI");

		String firmwarePath = deviceModel.getFirmwareUrl();
		if (firmwarePath == null) {
			MessageEntity messageEntity = new MessageEntity(DictEnum.MessageEntity.FIRMWARE_FILE_IS_NOT_EXIST,
					"固件文件不存在");
			return JSON.toJSONString(messageEntity);
		}
		JSONObject jsonObject = new JSONObject();
		String currentFirmwareVer = deviceInfoEntity.getCurrentFirmwareVer();
		currentFirmwareVer = currentFirmwareVer == null ? "" : currentFirmwareVer;
		firmwarePath = currentFirmwareVer + "/" + firmwarePath;
		// if(DictEnum.FirmwareVarEntity.TC_2G.equals(currentFirmwareVer)) {
		// firmwarePath = "tc2g/" + firmwarePath;
		// }else if(DictEnum.FirmwareVarEntity.TC_4G.equals(currentFirmwareVer)) {
		// firmwarePath = "tc4g/" + firmwarePath;
		// }
		RemoteUpgrade remoteUpgrade = new RemoteUpgrade(firmwarePath);
		int length = remoteUpgrade.getUpgradeByteLength();
		if (length <= 0) {
			MessageEntity messageEntity = new MessageEntity(DictEnum.MessageEntity.FIRMWARE_FILE_IS_NOT_EXIST,
					"固件文件不存在");
			return JSON.toJSONString(messageEntity);
		}
		String h = ByteHelper.intTo16Length8(length);
		if(!"".equals(remoteUpgrade.getCrc16())) {
			jsonObject.put("crc", remoteUpgrade.getCrc16());
		} else {
			MessageEntity messageEntity = new MessageEntity(DictEnum.MessageEntity.FIRMWARE_FILE_IS_NOT_EXIST, "crc校验为空");
			return JSON.toJSONString(messageEntity);
		}
		jsonObject.put("h", h);
		jsonObject.put("o", 6);
		jsonObject.put("m", deviceInfoEntity.getDeviceMac());
		SendMessageTransparentThread sendMessageTransparentThread = new SendMessageTransparentThread(appId, deviceIMEI,
				jsonObject.toJSONString(), 0);
		NettyChannelMapUtils.sendTaskThreadPool.execute(sendMessageTransparentThread);
		MessageEntity messageEntity = new MessageEntity(DictEnum.MessageEntity.SUCCESS, "指令下发成功");
		log.debug(logHeader + "指令下发成功。");
		return JSON.toJSONString(messageEntity);

	}

	/**
	 * 重启设备
	 * 
	 * @param logHeader
	 * @param json
	 * @param deviceIMEI
	 * @param deviceInfoEntity
	 * @return
	 * @throws Exception
	 */
	private String rebootDevice(String appId, String logHeader, JSONObject json, String deviceIMEI,
			DeviceInfoEntity deviceInfoEntity) throws Exception {
		String content;
		JSONObject jsonObject = new JSONObject();
		// 构造重启指令
		jsonObject.put("m", deviceInfoEntity.getDeviceMac());
		jsonObject.put("o", 9);
		SendMessageTransparentThread sendMessageTransparentThread = new SendMessageTransparentThread(appId, deviceIMEI,
				jsonObject.toJSONString(), 0);
		NettyChannelMapUtils.sendTaskThreadPool.execute(sendMessageTransparentThread);
		MessageEntity MessageEntity = new MessageEntity(DictEnum.MessageEntity.SUCCESS, "指令下发成功");
		content = JSON.toJSONString(MessageEntity);
		// 重启设备，把所有通道的回调参数清空
//		NettyChannelMapUtils.remove(appId, deviceIMEI);
		log.debug(logHeader + "指令下发成功。");
		return content;
	}

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
		String channelId = ctx.channel().id().asShortText();// 连接ID
		ConsoleChannelMap.remove(channelId);
		log.error(cause.getMessage(), cause);
		ctx.close();
	}

	@Override
	public void channelInactive(ChannelHandlerContext ctx) throws Exception {
		String remoteIpAddress = ((InetSocketAddress) ctx.channel().remoteAddress()).getAddress().getHostAddress();
		String channelId = ctx.channel().id().asShortText();// 连接ID

		log.info("[server][controller][" + remoteIpAddress + "]控制系统连接被断开。");

		ConsoleChannelMap.remove(channelId);
		ctx.close();
	}

}